package com.uinnova.product.eam.web.eam.mvc.diagram;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.framework.web.RemoteResult;
import com.binary.jdbc.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.bean.AutoLayoutDiagramConfVO;
import com.uinnova.product.eam.comm.model.es.AutoLayoutDiagramConf;
import com.uinnova.product.eam.model.AutoDiagramCiVo;
import com.uinnova.product.eam.model.EamAutoLayoutCdt;
import com.uinnova.product.eam.model.diagram.DiagramNodeLinkInfo;
import com.uinnova.product.eam.model.dto.RltDataAnalyzeDto;
import com.uinnova.product.eam.model.enums.RltPositionEnum;
import com.uinnova.product.eam.model.vo.CiSimpleInfoVo;
import com.uinnova.product.eam.model.vo.EamAnalyseCiVo;
import com.uinnova.product.eam.service.AutoLayoutDiagramSvc;
import com.uinnova.product.eam.service.ICIRltSwitchSvc;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.impl.IamsCIRltPrivateSvc;
import com.uinnova.product.eam.service.utils.VisualModelUtils;
import com.uinnova.product.vmdb.comm.doc.annotation.ModDesc;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiAttrDef;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uino.api.client.cmdb.ICIClassApiSvc;
import com.uino.api.client.cmdb.IRltClassApiSvc;
import com.uino.bean.cmdb.base.*;
import com.uino.bean.cmdb.business.BindCiRltRequestDto;
import com.uino.bean.cmdb.business.ClassNodeInfo;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.dao.cmdb.ESVisualModelSvc;
import com.uino.service.cmdb.microservice.IRltClassSvc;
import com.uino.util.sys.BeanUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("autoLayout")
@Slf4j
public class AutoLayoutConfMvc {


    @Resource
    private ESVisualModelSvc esVisualModelSvc;
    @Resource
    private ICIClassApiSvc classSvc;

    @Resource
    private IRltClassApiSvc rltClassApiSvc;

    @Resource
    private AutoLayoutDiagramSvc autoLayoutDiagramSvc;

    @Resource
    private ICIRltSwitchSvc rltSwitchSvc;

    @Resource
    private IRltClassSvc rltClassSvc;

    @Resource
    ICISwitchSvc ciSwitchSvc;

    @Resource
    IEamCIClassApiSvc ciClassApiSvc;

    @Autowired
    private IamsCIRltPrivateSvc iamsCiRltPrivateSvc;

    @GetMapping("getClassTree")
    public RemoteResult classTree() {
        List<ClassNodeInfo> classTree = classSvc.getClassTree();
        // 获取元模型中分类，过滤不存在分类
        // 获取当前使用的元模型信息
        ESVisualModel esVisualModel = esVisualModelSvc.getEnableModel(SysUtil.getCurrentUserInfo().getDomainId());
        if (BinaryUtils.isEmpty(esVisualModel.getJson())) {
            classTree = Collections.emptyList();
        }
        // 获取元模型中存在的ClassId
        Set<Long> classIdList = VisualModelUtils.getCiClassIds(esVisualModel);
        for (ClassNodeInfo classNodeInfo : classTree) {
            List<ClassNodeInfo> children = classNodeInfo.getChildren();
            children.removeIf(next -> next.getType().equals("class") && !classIdList.contains(next.getId()));
        }
        classTree.removeIf(classNodeInfo -> CollectionUtils.isEmpty(classNodeInfo.getChildren()));
        return new RemoteResult(classTree);
    }

    @GetMapping("getRltByClassId")
    public RemoteResult getRltAndClassByClass(@RequestParam Long classId) {
        ESVisualModel esVisualModel = esVisualModelSvc.getEnableModel(SysUtil.getCurrentUserInfo().getDomainId());
        List<CcCiClassInfo> result = Collections.emptyList();
        List<DiagramNodeLinkInfo> rltLinkList = VisualModelUtils.getRltClassIds(esVisualModel);
        List<Long> rltClassIds = new ArrayList<>();
        for (DiagramNodeLinkInfo each : rltLinkList) {
            if (each.getSourceId().equals(classId) || each.getTargetId().equals(classId)) {
                rltClassIds.add(each.getLinkId());
            }
        }
        if (!CollectionUtils.isEmpty(rltClassIds)) {
            CCcCiClass cdt = new CCcCiClass();
            cdt.setIds(rltClassIds.toArray(new Long[0]));
            result = rltClassApiSvc.getRltClassByCdt(cdt);

        }
        return new RemoteResult(result);
    }

    @PostMapping("getClassListByRlt")
    public RemoteResult getClassListByRlt(@RequestBody JSONObject param) {
        Long ciClassId = param.getLong("ciClassId");
        BinaryUtils.checkEmpty(ciClassId, "ciClassId");
        Object rltClassIdObj = param.get("rltClassIds");
        List<Long> rltClassIds = Collections.emptyList();
        if (!BinaryUtils.isEmpty(rltClassIdObj)) {
            rltClassIds = JSONArray.parseArray(JSONObject.toJSONString(rltClassIdObj), Long.class);
        }

        ESVisualModel esVisualModel = esVisualModelSvc.getEnableModel(SysUtil.getCurrentUserInfo().getDomainId());
        List<CcCiClassInfo> result = Collections.emptyList();
        Set<Long> classIds = new HashSet<>();
        List<DiagramNodeLinkInfo> rltLinkList = VisualModelUtils.getRltClassIds(esVisualModel);
        // 自己连接自己
        boolean flag = false;
        for (DiagramNodeLinkInfo link : rltLinkList) {
            if(!rltClassIds.contains(link.getLinkId())){
                continue;
            }
            if (link.getSourceId().equals(ciClassId) || link.getTargetId().equals(ciClassId)) {
                classIds.add(link.getSourceId());
                classIds.add(link.getTargetId());
            }
            if (link.getSourceId().equals(ciClassId) && link.getTargetId().equals(ciClassId)) {
                flag = true;
            }
        }
        if (!flag) {
            classIds.remove(ciClassId);
        }
        if (!CollectionUtils.isEmpty(classIds)) {
            CCcCiClass cdt = new CCcCiClass();
            cdt.setIds(classIds.toArray(new Long[0]));
            result = classSvc.queryClassByCdt(cdt);
        }
        return new RemoteResult(result);
    }

    @PostMapping("savaOrUpdate")
    public RemoteResult savaOrUpdate(@RequestBody AutoLayoutDiagramConf layoutDiagramConf) {
        BinaryUtils.checkEmpty(layoutDiagramConf, "配置信息");
        Long id = autoLayoutDiagramSvc.savaOrUpdate(layoutDiagramConf);
        return new RemoteResult(id);
    }

    @GetMapping("getConfByDiagramIdAndSheetId")
    public RemoteResult getConfByDiagramIdAndSheetId(@RequestParam String diagramId, String sheetId) {
        BinaryUtils.checkEmpty(diagramId, "视图ID");
        BinaryUtils.checkEmpty(sheetId, "页面ID");
        AutoLayoutDiagramConfVO result = new AutoLayoutDiagramConfVO();
        AutoLayoutDiagramConf autoLayoutDiagramConf = autoLayoutDiagramSvc.getConfByDiagramIdAndSheetId(diagramId, sheetId);
        // 封装分类信息
        if (!BinaryUtils.isEmpty(autoLayoutDiagramConf)) {
            BeanUtil.copyProperties(autoLayoutDiagramConf, result);
            if (!BinaryUtils.isEmpty(result.getClassCode())) {
                CCcCiClass cdt = new CCcCiClass();
                cdt.setClassCodes(new String[]{result.getClassCode()});
                List<CcCiClassInfo> ciClassInfoList = classSvc.queryClassByCdt(cdt);
                if (!CollectionUtils.isEmpty(ciClassInfoList)) {
                    result.setClassInfo(ciClassInfoList.get(0));
                }
            }
            if (!BinaryUtils.isEmpty(result.getCiCode())) {
                List<ESCIInfo> ciByCodes = ciSwitchSvc.getCiByCodes(Lists.newArrayList(result.getCiCode()), null, LibType.DESIGN);
                if (!CollectionUtils.isEmpty(ciByCodes)) {
                    result.setCentreCiInfo(ciByCodes.get(0));
                }
            }
            if (!CollectionUtils.isEmpty(result.getRltClassIds())) {
                CCcCiClass cdt = new CCcCiClass();
                cdt.setIds(result.getRltClassIds().toArray(new Long[]{}));
                List<CcCiClassInfo> rltClassList = rltClassSvc.getRltClassByCdt(cdt);
                result.setRltClassInfoList(rltClassList);
            }
            if (!CollectionUtils.isEmpty(result.getClassCodes())) {
                CCcCiClass cdt = new CCcCiClass();
                cdt.setClassCodes(result.getClassCodes().toArray(new String[0]));
                List<CcCiClassInfo> ciClassInfoList = classSvc.queryClassByCdt(cdt);
                result.setTargetClassInfoList(ciClassInfoList);
            }
        }
        return new RemoteResult(result);
    }

    @PostMapping("queryCIAndRltInfo")
    public RemoteResult queryCIAndRltInfo(@RequestBody JSONObject param) {
        String centreCiCode = param.getString("centreCiCode");
        Object rltClassIdObj = param.get("rltClassIds");
        List<Long> rltClassIds = Collections.emptyList();
        if (!BinaryUtils.isEmpty(rltClassIdObj)) {
            rltClassIds = JSONArray.parseArray(JSONObject.toJSONString(rltClassIdObj), Long.class);
        }
        List<Long> classIds = Collections.emptyList();
        Object classIdObj = param.get("classIds");
        if (!BinaryUtils.isEmpty(classIdObj)) {
            classIds = JSONArray.parseArray(JSONObject.toJSONString(classIdObj), Long.class);
        }
        Map<String, Object> result = queryCIAndRltInfo(centreCiCode, rltClassIds, classIds);
        return new RemoteResult(result);
    }

    private Map<String, Object> queryCIAndRltInfo(String centreCiCode, List<Long> rltClassIds, List<Long> classIds) {
        if (BinaryUtils.isEmpty(centreCiCode)) {
            throw new BinaryException("中心元素不可为空");
        }
        CCcCi cdt = new CCcCi();
        cdt.setCiCodes(new String[]{centreCiCode});
        List<CcCiInfo> ccCiInfos = ciSwitchSvc.queryCiInfoList(SysUtil.getCurrentUserInfo().getDomainId(), cdt, "", false, false, LibType.DESIGN);
        if (CollectionUtils.isEmpty(ccCiInfos)) {
            throw new BinaryException("中心元素不存在");
        }
        CcCiInfo ccCiInfo = ccCiInfos.get(0);
        CcCiClassInfo classInfo = classSvc.queryClassInfoById(ccCiInfo.getCi().getClassId());
        Map<String, Object> result = MapUtil.newHashMap(3);
        if (CollectionUtils.isEmpty(rltClassIds) || CollectionUtils.isEmpty(classIds)) {
            result.put("sourceList", Collections.emptyList());
            result.put("targetList", Collections.emptyList());
            result.put("centreCi", ccCiInfos.get(0));
            result.put("centreClassInfo", classInfo);
            return result;
        }
        //左侧 查询源端
        List<RltDataAnalyzeDto> leftSourceList = queryLeftRltAndCi(centreCiCode, rltClassIds, classIds, 1);
        //右侧 查询目标端
        List<RltDataAnalyzeDto> rightTargetList = queryLeftRltAndCi(centreCiCode, rltClassIds, classIds, 2);
        result.put("sourceList", leftSourceList);
        result.put("targetList", rightTargetList);
        result.put("centreCi", ccCiInfos.get(0));
        result.put("centreClassInfo", classInfo);
        return result;
    }

    private List<RltDataAnalyzeDto> queryLeftRltAndCi(String centreCiCode, List<Long> rltClassIds, List<Long> classIds, int position) {
        List<RltDataAnalyzeDto> result = new ArrayList<>();
        ESRltSearchBean bean = new ESRltSearchBean();
        bean.setPageNum(1);
        bean.setPageSize(1000);
        if (RltPositionEnum.LEFT.getVal() == position) {
            bean.setSourceCiCodes(Sets.newHashSet(centreCiCode));
            bean.setTargetClassIds(classIds);
        } else {
            bean.setTargetCiCodes(Sets.newHashSet(centreCiCode));
            bean.setSourceClassIds(classIds);
        }
        bean.setRltClassIds(rltClassIds);
        Page<CcCiRltInfo> rltInfoPage = rltSwitchSvc.searchRltByBean(bean, LibType.DESIGN);
        List<CcCiRltInfo> data = rltInfoPage.getData();
        List<CcCiRltInfo> ciRltInfoList = new ArrayList<>(data);
        if (rltInfoPage.getTotalPages() > 1) {
            for (int i = 2; i < rltInfoPage.getTotalPages() + 1; i++) {
                bean.setPageNum(i);
                Page<CcCiRltInfo> rltInfoPage2 = rltSwitchSvc.searchRltByBean(bean, LibType.DESIGN);
                if (CollectionUtils.isEmpty(rltInfoPage2.getData())) {
                    ciRltInfoList.addAll(rltInfoPage2.getData());
                }
            }
        }
        if (!CollectionUtils.isEmpty(ciRltInfoList)) {
            Set<Long> rltClassIdList = ciRltInfoList.stream().map(each -> each.getCiRlt().getClassId()).collect(Collectors.toSet());
            //获取关系分类映射
            Map<Long, CcCiClassInfo> rltClassMap = getRltClassMap(rltClassIdList);
            Map<String, CcCiInfo> privateSyncSaveCiMap = new HashMap<>();
            List<BindCiRltRequestDto> rltRequestDtos = new ArrayList<>();
            String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
            ciRltInfoList.forEach(rltInfo -> {
                privateSyncSaveCiMap.put(rltInfo.getSourceCiInfo().getCi().getCiCode(), rltInfo.getSourceCiInfo());
                privateSyncSaveCiMap.put(rltInfo.getTargetCiInfo().getCi().getCiCode(), rltInfo.getTargetCiInfo());
            });
            // ci
            List<CcCiInfo> syncCiList = new ArrayList<>(privateSyncSaveCiMap.values());
            Map<String, ? extends SaveBatchCIContext> contextMap = saveOrUpdateCiByDesignCIInfo(syncCiList, loginCode);
            List<ESCIInfo> dbCiList = contextMap.values().stream().map(SaveBatchCIContext::getEsCi).collect(Collectors.toList());
            Map<String, ESCIInfo> esciInfoMap = dbCiList.stream().collect(Collectors.toMap(CcCi::getCiCode, esciInfo -> esciInfo, (k1, k2) -> k1));
            List<ESCIClassInfo> classList = ciClassApiSvc.selectCiClassByIds(classIds);
            Map<Long, ESCIClassInfo> classMap = classList.stream().collect(Collectors.toMap(ESCIClassInfo::getId, each -> each));
            for (CcCiRltInfo rltInfo : ciRltInfoList) {
                ESCIInfo privateSource = esciInfoMap.get(rltInfo.getCiRlt().getSourceCiCode());
                ESCIInfo privateTarget = esciInfoMap.get(rltInfo.getCiRlt().getTargetCiCode());
                if (BinaryUtils.isEmpty(privateSource)) {
                    log.error("源端ci为找到：ciCode={}", rltInfo.getCiRlt().getSourceCiCode());
                    continue;
                }
                if (BinaryUtils.isEmpty(privateTarget)) {
                    log.error("目标端ci为找到：ciCode={}", rltInfo.getCiRlt().getTargetCiCode());
                    continue;
                }
                BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(loginCode).repetitionError(false).rltClassId(rltInfo.getCiRlt().getClassId())
                        .custom1(rltInfo.getCiRlt().getCustom1()).sourceCiId(privateSource.getId()).targetCiId(privateTarget.getId()).build();
                rltRequestDtos.add(bindRlt);
            }
            // 转换为私有库数据
            // 关系bindCiRltBatch
            List<ESCIRltInfo> ciRltInfos = iamsCiRltPrivateSvc.bindBatchCiRltNoFilter(rltRequestDtos);
            if(CollectionUtils.isEmpty(ciRltInfos)){
                return result;
            }
            Map<String, ESCIRltInfo> rltInfoMap = ciRltInfos.stream().collect(Collectors.toMap(ESCIRltInfo::getCiCode, e -> e, (k1, k2) -> k1));
            for (CcCiRltInfo rltInfo : ciRltInfoList) {
                RltDataAnalyzeDto analyze = new RltDataAnalyzeDto();
                CcCiInfo ciInfo;
                if (RltPositionEnum.LEFT.getVal() == position) {
                    ciInfo = rltInfo.getTargetCiInfo();
                } else {
                    ciInfo = rltInfo.getSourceCiInfo();
                }
//                过滤源端和目标端ci相同的关系
                if (ciInfo.getCi().getCiCode().equals(centreCiCode)) {
                    continue;
                }
                ESCIInfo esciInfo = esciInfoMap.get(ciInfo.getCi().getCiCode());
                if(esciInfo == null){
                    continue;
                }
                ESCIClassInfo esciClassInfo = classMap.get(esciInfo.getClassId());
                CcCiInfo ccCiInfo = EamUtil.coverESCIInfo(esciInfo, EamUtil.copy(esciClassInfo.getAttrDefs(), CcCiAttrDef.class), esciClassInfo);
                ccCiInfo.setFixMapping(null);
                EamAnalyseCiVo copy = EamUtil.copy(ccCiInfo, EamAnalyseCiVo.class);
                analyze.setNodeInfo(copy);
                String ciCode = rltInfo.getCiRlt().getCiCode();
                analyze.setRltCode(ciCode);
                ESCIRltInfo esciRltInfo = rltInfoMap.get(ciCode);
                analyze.setRltInfo(esciRltInfo);
                analyze.setRltClassInfo(rltClassMap.get(rltInfo.getCiRlt().getClassId()));
                result.add(analyze);
            }
        }
        return result;
    }

    private Map<String, ? extends SaveBatchCIContext> saveOrUpdateCiByDesignCIInfo(List<CcCiInfo> syncCiList, String loginCode) {
        List<String> ciCodes = syncCiList.stream().map(e -> e.getCi().getCiCode()).collect(Collectors.toList());
        ESCISearchBean esciSearchBean = new ESCISearchBean();
        esciSearchBean.setPageNum(1);
        esciSearchBean.setPageSize(ciCodes.size());
        esciSearchBean.setOwnerCode(loginCode);
        esciSearchBean.setCiCodes(new ArrayList<>(ciCodes));
        Page<ESCIInfo> ciPrivate = ciSwitchSvc.searchESCIByBean(esciSearchBean, LibType.PRIVATE);
        Map<String, ESCIInfo> prvateCiMap = ciPrivate.getData().stream().collect(Collectors.toMap(CcCi::getCiCode, e -> e, (k1, k2) -> k1));
        Set<Long> classIds = new HashSet<>();
        for (CcCiInfo ccCiInfo : syncCiList) {
            ccCiInfo.getCi().setId(null);
            ccCiInfo.getCi().setOwnerCode(loginCode);
            if (prvateCiMap.containsKey(ccCiInfo.getCi().getCiCode())) {
                ccCiInfo.getCi().setId(prvateCiMap.get(ccCiInfo.getCi().getCiCode()).getId());
            }
            classIds.add(ccCiInfo.getCi().getClassId());
        }

        return ciSwitchSvc.saveOrUpdateBatchCI(EamUtil.coverCiInfoList(syncCiList), new ArrayList<>(classIds), loginCode, loginCode, LibType.PRIVATE);
    }

    private Map<Long, CcCiClassInfo> getRltClassMap(Set<Long> rltClassIds) {
        CCcCiClass cdt = new CCcCiClass();
        cdt.setIds(rltClassIds.toArray(new Long[]{}));
        List<CcCiClassInfo> rltClassList = rltClassSvc.getRltClassByCdt(cdt);
        return rltClassList.stream().collect(Collectors.toMap(ccCiClassInfo -> ccCiClassInfo.getCiClass().getId(), ccCiClassInfo -> ccCiClassInfo, (k1, k2) -> k1));
    }

    @GetMapping("/business/element")
    @ModDesc(desc = "业务能力图-获取架构分类", pDesc = "分类id", pType = Long.class, rDesc = "下级分类集合", rType = RemoteResult.class)
    public RemoteResult getBusinessElement(@RequestParam(required = false) Long classId) {
        List<CiSimpleInfoVo> result = autoLayoutDiagramSvc.getBusinessElement(classId);
        return new RemoteResult(result);
    }

    @PostMapping("/business/data")
    @ModDesc(desc = "业务能力图-获取架构元素", pDesc = "分类id", pType = Long.class, rDesc = "下级分类集合", rType = RemoteResult.class)
    public RemoteResult getBusinessData(@RequestBody List<EamAutoLayoutCdt> cdt) {
        List<AutoDiagramCiVo> result = autoLayoutDiagramSvc.getBusinessData(cdt);
        return new RemoteResult(result);
    }
}
